package org.springframework.beans.factory.util;

import org.springframework.beans.factory.anno.*;
import org.springframework.beans.factory.config.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.core.logger.AbstractLogger;
import org.springframework.core.logger.LoggerFactory;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;

import java.io.File;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.util.Objects;
import java.util.*;

/**
 * @author:刘仁有
 * @desc:
 * @email:953506233@qq.com
 * @data:2019/12/13
 */
public class ConfigurationClassUtils {
    private static final String CONFIGURATION_CLASS_FULL = "full";

    private static final String CONFIGURATION_CLASS_LITE = "lite";

    private static final String CONFIGURATION_CLASS_ATTRIBUTE = "ConfigurationClassPostProcessor.configurationClass";

    public static final String CONFIGURATION_VALUE = "value";

    private static final Set<Class<? extends Annotation>> candidateIndicators = new HashSet<>(8);

    private static final AbstractLogger logger = LoggerFactory.getLogger(ConfigurationClassUtils.class);

    private static final List<String>classNames = new ArrayList<String>();
    static {
        candidateIndicators.add(Component.class);
        candidateIndicators.add(ComponentScan.class);
        candidateIndicators.add(Import.class);
    }

    public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef) {
        AnnotationMetadata metadata = null;
        //如果是加了注解的bd
        if (beanDef instanceof AnnotatedBeanDefinition) {
            metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
        }
        else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).getBeanClass()!=null) {
            Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
            metadata = new StandardAnnotationMetadata(beanClass);
        }

        //@Configuration->FULL
        if (isFullConfigurationCandidate(metadata)) {
            beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
        }
        // @Import @Component @ComponentScan->LITE
        else if (isLiteConfigurationCandidate(metadata)) {
            beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
        }
        else {
            return false;
        }

        return true;
    }

    public static boolean checkConfigurationClassCandidate(Class<?> clazz) {
        Objects.requireNonNull(clazz,"clazz must not null");
        AnnotationMetadata metadata = new StandardAnnotationMetadata(clazz);

        //@Configuration->FULL
        if (isFullConfigurationCandidate(metadata)) {
            return true;
        }
        // @Import @Component @ComponentScan->LITE
        else if (isLiteConfigurationCandidate(metadata)) {
            return true;
        }
        else {
            return false;
        }
    }

    private static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
        return metadata.isAnnotated(Configuration.class);
    }

    private static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
        if (metadata.isInterface()) {
            return false;
        }

        // Any of the typical annotations found?
        for (Class<? extends Annotation> indicator : candidateIndicators) {
            if (metadata.isAnnotated(indicator)) {
                return true;
            }
        }

        // Finally, let's look for @Bean methods...
        try {
            return metadata.hasAnnotatedMethods(Bean.class);
        }
        catch (Throwable ex) {
                logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
            return false;
        }
    }

    public static boolean isFullConfigurationClass(BeanDefinition bd) {
        return CONFIGURATION_CLASS_FULL.equals(bd.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
    }

    public static boolean isLiteConfigurationClass(BeanDefinition bd) {
        return CONFIGURATION_CLASS_LITE.equals(bd.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
    }


    public static List<String> basePackageScan(String packName){

        URL url = ConfigurationClassUtils.class.getClassLoader().getResource(packName.replaceAll("\\.", "/"));
        String fileUrl = url.getFile()+"/";// E:/workspace/com/spring/mvc/
        File file = new File(fileUrl);//把路径转为文件目录
        String[] fileStr = file.list();//列出文件目录所有文件和文件夹的名字
        for (String path : fileStr) {//遍历所有的文件或文件夹的名字
            File pathFile = new File(fileUrl+path);//根据url和名字拼装成完整的url路径E:/workspace/com/spring/mvc
            if(pathFile.isDirectory()){
                basePackageScan(packName+"."+path);
            }else{//class 文件
                //mybatis.mapper.UserMapper
                classNames.add(packName+"."+pathFile.getName().replace(".class", ""));
            }
        }
        return classNames;
    }

}
